package org.nbict.iot.persist.repo;

import com.mongodb.*;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.InsertManyOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.nbict.iot.PropertyUtil;
import org.nbict.iot.persist.Repo;
import org.nbict.iot.trident.mogo.codec.BigDecimalCodec;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Created by songseven on 18/6/14.
 */
public class MongoRepo implements Repo {

//    private static final String DB_NAME = "iot_car_test";
//    private static final String DB_USER_NAME = "nbicc";
//    private static final String DB_PSW = "43b5fda47f34442091351698b9136e87";
//    private static final String DB_ADMIN_NAME = "admin";
//    private static final String DB_IP_PORT = "122.112.237.203:27011";

    private String database;
    private String username;
    private String password;
    private String adminname;
    private String url;

    private MongoClient mongoClient;

    public MongoRepo() {
        init(); //debug
    }

    private void init() {
        database = PropertyUtil.getProperty("mongo.database");
        username = PropertyUtil.getProperty("mongo.username");
        password = PropertyUtil.getProperty("mongo.password");
        adminname = PropertyUtil.getProperty("mongo.admin.name");
        url = PropertyUtil.getProperty("mongo.url");

        MongoClientOptions.Builder build = new MongoClientOptions.Builder();
        build.connectionsPerHost(50);   //与目标数据库能够建立的最大connection数量为50
//        build.autoConnectRetry(true);   //自动重连数据库启动
        build.threadsAllowedToBlockForConnectionMultiplier(50); //如果当前所有的connection都在使用中，则每个connection上可以有50个线程排队等待
        build.maxWaitTime(1000*60*2);
        build.connectTimeout(1000*60*1);    //与数据库建立连接的timeout设置为1分钟
        build.socketTimeout(1000*60*1);
        CodecRegistry codecRegistry = CodecRegistries.fromRegistries(MongoClient.getDefaultCodecRegistry(),CodecRegistries.fromCodecs(new BigDecimalCodec())); //important
        MongoClientOptions myOptions = build.codecRegistry(codecRegistry).build();
        try {
            //数据库连接实例
            MongoCredential credential = MongoCredential.createCredential(username, adminname, password.toCharArray());
            mongoClient = new MongoClient(new ServerAddress(url), Arrays.asList(credential),myOptions);

        } catch (MongoException e) {
            e.printStackTrace(); //log
        }
    }

    @Deprecated
    public boolean insert(String collectionName, BasicDBObject object){
        MongoDatabase db = mongoClient.getDatabase(database);
        MongoCollection<BasicDBObject> collection = db.getCollection(collectionName, BasicDBObject.class);
        boolean result = true;
        try{
            collection.insertOne(object);
        }catch(Exception e){
            e.printStackTrace();
            result = false;
        }finally{
            return result;
        }
    }

    public boolean upsert(String collectionName, Map<String, Object> queryMap, BasicDBObject object){
        UpdateOptions options = new UpdateOptions().upsert(true); //插入更新
        MongoDatabase db = mongoClient.getDatabase(database);
        MongoCollection<BasicDBObject> collection = db.getCollection(collectionName, BasicDBObject.class);
        Document queryDocument = new Document(queryMap);
        UpdateResult result = collection.replaceOne(queryDocument, object, options);
        return result.wasAcknowledged();
    }

    public BasicDBObject get(String collectionName,Map<String, Object> queryMap){
        List<BasicDBObject> result = find(collectionName, queryMap, null);
        if(!result.isEmpty() && result.size() == 1){
            return result.get(0);
        }
        return null;
    }

    @Deprecated
    public List<BasicDBObject> find(String collectionName, Map<String, Object> queryMap, BasicDBObject sort){
        List<BasicDBObject> result = new ArrayList<BasicDBObject>();
        MongoDatabase db = mongoClient.getDatabase(database);
        MongoCollection<BasicDBObject> collection = db.getCollection(collectionName, BasicDBObject.class);
        Document queryDocument = new Document(queryMap);
        if (sort != null) {
            collection.find(queryDocument).sort(sort).into(result);
        } else {
            collection.find(queryDocument).into(result);
        }
        return result;
    }

    public void insert(String collectionName, List<Document> documents,
                       boolean ordered) {
        InsertManyOptions options = new InsertManyOptions();
        if (!ordered) {
            options.ordered(false);
        }
        MongoDatabase db = mongoClient.getDatabase(database);
        MongoCollection<Document> collection = db.getCollection(collectionName);
        collection.insertMany(documents, options);
    }

    public void insert(String collectionName, Document document) {
        MongoDatabase db = mongoClient.getDatabase(database);
        MongoCollection<Document> collection = db.getCollection(collectionName);
        collection.insertOne(document);
    }

    public List<Document> list(String collectionName, Bson query, Bson sort){
        List<Document> result = new ArrayList<Document>();
        MongoDatabase db = mongoClient.getDatabase(database);
        MongoCollection<Document> collection = db.getCollection(collectionName);
        if (query!= null && sort != null) {
            collection.find(query).sort(sort).into(result);
        } else {
            collection.find().into(result);
        }
        return result;
    }

    public MongoClient getMongoClient() {
        return mongoClient;
    }

    public MongoRepo setMongoClient(MongoClient mongoClient) {
        this.mongoClient = mongoClient;
        return this;
    }
}
